#ifndef __XGU_DELEGATE00_H__
#define __XGU_DELEGATE00_H__

#include "Functor.h"
#include "MemberFunctor.h"
#include "FunctorBinder.h"

#include <vector>
using std::vector;

#include <cassert>

namespace xgu
{
	// -----------------------------------------------------------
	// No Argument
	template <typename TRet>
	class Delegate<TRet>
	{
	public:
		typedef vector<TRet> ReturnType;

		Delegate() {}
		
		virtual ~Delegate()
		{
			for (size_t i = 0; i < delegates.size(); ++i)
			{
				assert(delegates[i]);
				delete delegates[i];
				delegates[i] = NULL;
			}
		}


		Delegate(const Delegate<TRet>& delegate)
		{
			for (size_t i = 0; i < delegate.delegates.size(); ++i)
			{
				Add(delegate.delegates[i]);
			}
		}


		Delegate& operator=(const Delegate<TRet>& delegate)
		{
			RemoveAll();
			Add(delegate);
			return *this;
		}
		
		template <typename TClass>
		Delegate(TClass& C, TRet (TClass::*F)())
		{
			Add<TClass>(C, F);
		}
		
		template <typename TClass>
		Delegate(TClass* pC, TRet (TClass::*F)())
		{
			Add<TClass>(pC, F);
		}

		
		Delegate& operator += (const Delegate<TRet>& Value)
		{
			Add(Value);
			return *this;
		}
		
		Delegate& operator -= (const Delegate<TRet>& Value)
		{
			Remove(Value);
			return *this;
		}

		
		virtual vector<TRet> operator() () const
		{
			vector<TRet> ret;
			for (size_t i = 0; i < delegates.size(); ++i)
			{
				Functor<TRet>* f = delegates[i];
				assert(f);
				ret.push_back((*f)());
			}
			return ret;
		}

		
		template<typename TClass>
		void Add(TClass& C, TRet (TClass::*F)())
		{
			Functor<TRet>* f = new MemberFunctor<TClass, TRet>(C, F);
			assert(f);
			delegates.push_back(f);
		}
		
		template<typename TClass>
		void Add(TClass* pC, TRet (TClass::*F)())
		{
			assert(pC);
			Functor<TRet>* f = new MemberFunctor<TClass, TRet>(*pC, F);
			assert(f);
			delegates.push_back(f);
		}
		
		void Add(const Delegate<TRet>& D)
		{
			for (size_t i = 0; i < D.delegates.size(); ++i)
			{
				Add(D.delegates[i]);
			}
		}
		
		void Remove(const Delegate<TRet>& D)
		{
			if (&D == this)
				RemoveAll();
			else
				for (size_t i = 0; i < D.delegates.size(); ++i)
				{
					Functor<TRet>* f = D.delegates[i];
					assert(f);
					Remove(f);
				}
		}

		void RemoveAll()
		{
			for (size_t i = 0; i < delegates.size(); ++i)
			{
				assert(delegates[i] != NULL);
				delete delegates[i];
				delegates[i] = NULL;
			}
			delegates.clear();
		}

		// Arguments Binding

		template <typename TClass, typename TArg0>
		Delegate(TClass& c, TRet (TClass::*F)(TArg0), const TArg0& arg0)
		{
			Add(c, F, arg0);
		}

		
		template <typename TClass, typename TArg0>
		void Add(TClass& c, TRet (TClass::*F)(TArg0), const TArg0& arg0)
		{
			Functor<TRet>* f = new FunctorBinder<TRet, TArg0>(c, F, arg0);
			assert(f != NULL);
			delegates.push_back(f);
		}
		
		template <typename TClass, typename TArg0>
		void Remove(TClass& c, TRet (TClass::*F)(TArg0), const TArg0& arg0)
		{
			Remove(&FunctorBinder<TRet, TArg0>(c, F, arg0));
		}

		
		template <typename TClass, typename TArg0, typename TArg1>
		void Add(TClass& c, TRet (TClass::*F)(TArg0, TArg1), const TArg0& arg0, const TArg1& arg1)
		{
			Functor<TRet>* f = new FunctorBinder<TRet, TArg0, TArg1>(c, F, arg0, arg1);
			assert(f != NULL);
			delegates.push_back(f);
		}
		
		template <typename TClass, typename TArg0, typename TArg1>
		void Remove(TClass& c, TRet (TClass::*F)(TArg0, TArg1), const TArg0& arg0, const TArg1& arg1)
		{
			Remove(&FunctorBinder<TRet, TArg0, TArg1>(c, F, arg0, arg1));
		}

		
		template <typename TClass, 
			typename TArg0, typename TArg1, typename TArg2, typename TArg3, typename TArg4,
			typename TArg5, typename TArg6, typename TArg7, typename TArg8, typename TArg9>
			void Add(TClass& c, 
				TRet (TClass::*F)(TArg0, TArg1, TArg2, TArg3, TArg4, 
								TArg5, TArg6, TArg7, TArg8, TArg9), 
				const TArg0& arg0, 
				const TArg1& arg1, 
				const TArg2& arg2, 
				const TArg3& arg3, 
				const TArg4& arg4, 
				const TArg5& arg5, 
				const TArg6& arg6, 
				const TArg7& arg7, 
				const TArg8& arg8, 
				const TArg9& arg9)
		{
			Functor<TRet>* f = new FunctorBinder<TRet,
				TArg0, TArg1, TArg2, TArg3, TArg4, 
				TArg5, TArg6, TArg7, TArg8, TArg9>
				(c, F, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
			assert(f != NULL);
			delegates.push_back(f);
		}
	
		template <typename TClass, 
			typename TArg0, typename TArg1, typename TArg2, typename TArg3, typename TArg4,
			typename TArg5, typename TArg6, typename TArg7, typename TArg8, typename TArg9>
		void Remove(TClass& c, 
			TRet (TClass::*F)(TArg0, TArg1, TArg2, TArg3, TArg4, 
							TArg5, TArg6, TArg7, TArg8, TArg9), 
			const TArg0& arg0, 
			const TArg1& arg1, 
			const TArg2& arg2, 
			const TArg3& arg3, 
			const TArg4& arg4, 
			const TArg5& arg5, 
			const TArg6& arg6, 
			const TArg7& arg7, 
			const TArg8& arg8, 
			const TArg9& arg9)
		{
			Remove(&FunctorBinder<TRet, TArg0, TArg1, TArg2, TArg3, TArg4, 
				TArg5, TArg6, TArg7, TArg8, TArg9>(c, F, arg0, arg1, arg2, arg3, arg4, arg5, 
				arg6, arg7, arg8, arg9));
		}

	
	protected:
		
		void Add(const Functor<TRet>* F)
		{
			assert(F);
			delegates.push_back(F->Clone());
		}
		
		void Remove(const Functor<TRet>* F)
		{
			assert(F);
			for (vector<Functor<TRet>*>::const_iterator iter = delegates.begin();
				iter != delegates.end(); ++iter)
			{
				const Functor<TRet>* f = *iter;
				assert(f);
				if (*f == *F)
				{
					delete *iter;
					delegates.erase(iter);
					break;
				}
			}
		}

	
	private:
		vector<Functor<TRet>*> delegates;
	};


	// ------------------------------------------------------------------
	// Void Return Type Specialization
	template <>
	class Delegate<void>
	{
		typedef void TRet;

	public:

		typedef void ReturnType;

		Delegate() {}
		virtual ~Delegate()
		{
			RemoveAll();
		}

		Delegate(const Delegate<TRet>& delegate)
		{
			Add(delegate);
		}
		Delegate& operator=(const Delegate<TRet>& delegate)
		{
			RemoveAll();
			Add(delegate);
			return *this;
		}

		template <typename TClass>
		Delegate(TClass& C, TRet (TClass::*F)())
		{
			Add<TClass>(C, F);
		}
		template <typename TClass>
		Delegate(TClass* pC, TRet (TClass::*F)())
		{
			Add<TClass>(pC, F);
		}

		Delegate& operator += (const Delegate<TRet>& Value)
		{
			Add(Value);
			return *this;
		}
		Delegate& operator -= (const Delegate<TRet>& Value)
		{
			Remove(Value);
			return *this;
		}

		virtual void operator() () const
		{
			for (size_t i = 0; i < delegates.size(); ++i)
			{
				Functor<TRet>* f = delegates[i];
				assert(f);
				(*f)();
			}
		}


		template<typename TClass>
		void Add(TClass& C, TRet (TClass::*F)())
		{
			Functor<TRet>* f = new MemberFunctor<TClass, TRet>(C, F);
			assert(f);
			delegates.push_back(f);
		}
		template<typename TClass>
		void Add(TClass* pC, TRet (TClass::*F)())
		{
			assert(pC);
			Functor<TRet>* f = new MemberFunctor<TClass, TRet>(*pC, F);
			assert(f);
			delegates.push_back(f);
		}
		void Add(const Delegate<TRet>& D)
		{
			for (size_t i = 0; i < D.delegates.size(); ++i)
			{
				Add(D.delegates[i]);
			}
		}
		void Remove(const Delegate<TRet>& D)
		{
			for (size_t i = 0; i < D.delegates.size(); ++i)
			{
				Functor<TRet>* f = D.delegates[i];
				assert(f);
				Remove(f);
			}
		}

		void RemoveAll()
		{
			for (size_t i = 0; i < delegates.size(); ++i)
			{
				assert(delegates[i] != NULL);
				delete delegates[i];
				delegates[i] = NULL;
			}
			delegates.clear();
		}
		// -------------------------------------------------------
		// Arguments binding

		template <typename TClass, typename TArg0>
		Delegate(TClass& c, TRet (TClass::*F)(TArg0), const TArg0& arg0)
		{
			Add(c, F, arg0);
		}

		template <typename TClass, typename TArg0>
		void Add(TClass& c, TRet (TClass::*F)(TArg0), const TArg0& arg0)
		{
			Functor<TRet>* f = new FunctorBinder<TRet, TArg0>(c, F, arg0);
			assert(f != NULL);
			delegates.push_back(f);
		}
		template <typename TClass, typename TArg0>
		void Remove(TClass& c, TRet (TClass::*F)(TArg0), const TArg0& arg0)
		{
			Remove(&FunctorBinder<TRet, TArg0>(c, F, arg0));
		}

		template <typename TClass, typename TArg0, typename TArg1>
		void Add(TClass& c, TRet (TClass::*F)(TArg0, TArg1), const TArg0& arg0, const TArg1& arg1)
		{
			Functor<TRet>* f = new FunctorBinder<TRet, TArg0, TArg1>(c, F, arg0, arg1);
			assert(f != NULL);
			delegates.push_back(f);
		}
		template <typename TClass, typename TArg0, typename TArg1>
		void Remove(TClass& c, TRet (TClass::*F)(TArg0, TArg1), const TArg0& arg0, const TArg1& arg1)
		{
			Remove(&FunctorBinder<TRet, TArg0, TArg1>(c, F, arg0, arg1));
		}

		template <typename TClass, 
			typename TArg0, typename TArg1, typename TArg2, typename TArg3, typename TArg4,
			typename TArg5, typename TArg6, typename TArg7, typename TArg8, typename TArg9>
		void Add(TClass& c, 
			TRet (TClass::*F)(TArg0, TArg1, TArg2, TArg3, TArg4, 
							TArg5, TArg6, TArg7, TArg8, TArg9), 
			const TArg0& arg0, 
			const TArg1& arg1, 
			const TArg2& arg2, 
			const TArg3& arg3, 
			const TArg4& arg4, 
			const TArg5& arg5, 
			const TArg6& arg6, 
			const TArg7& arg7, 
			const TArg8& arg8, 
			const TArg9& arg9)
		{
			Functor<TRet>* f = new FunctorBinder<TRet, 
				TArg0, TArg1, TArg2, TArg3, TArg4, 
				TArg5, TArg6, TArg7, TArg8, TArg9>
				(c, F, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
			assert(f != NULL);
			delegates.push_back(f);
		}
		template <typename TClass, 
			typename TArg0, typename TArg1, typename TArg2, typename TArg3, typename TArg4,
			typename TArg5, typename TArg6, typename TArg7, typename TArg8, typename TArg9>
		void Remove(TClass& c, 
			TRet (TClass::*F)(TArg0, TArg1, TArg2, TArg3, TArg4, 
			TArg5, TArg6, TArg7, TArg8, TArg9), 
			const TArg0& arg0, 
			const TArg1& arg1, 
			const TArg2& arg2, 
			const TArg3& arg3, 
			const TArg4& arg4, 
			const TArg5& arg5, 
			const TArg6& arg6, 
			const TArg7& arg7, 
			const TArg8& arg8, 
			const TArg9& arg9)
		{
			Remove(&FunctorBinder<TRet, TArg0, TArg1, TArg2, TArg3, TArg4, 
				TArg5, TArg6, TArg7, TArg8, TArg9>(c, F, arg0, arg1, arg2, arg3, arg4, arg5, 
				arg6, arg7, arg8, arg9));
		}

	protected:

		void Add(const Functor<TRet>* F)
		{
			assert(F);
			delegates.push_back(F->Clone());
		}
		void Remove(const Functor<TRet>* F)
		{
			assert(F);
			for (vector<Functor<TRet>*>::const_iterator iter = delegates.begin();
				iter != delegates.end(); ++iter)
			{
				const Functor<TRet>* f = *iter;
				assert(f);
				if (*f == *F)
				{
					delete *iter;
					delegates.erase(iter);
					break;
				}
			}
		}


		template <typename TArg0>
		void Add(const Functor<TRet, TArg0>& F, TArg0 arg0)
		{
			Functor<TRet>* f = new FunctorBinder<TRet, TArg0>(F, arg0);
			assert(f != NULL);
			delegates.push_back(f);
		}
		template<typename TArg0>
		void Remove(const Functor<TRet, TArg0>& F, TArg0 arg0)
		{
			Remove(&FunctorBinder<TRet, TArg0>(F, arg0));
		}

	private:
		vector<Functor<TRet>*> delegates;
	};


}

#endif